R para Análise de Dados

Bruno Crotman

22/09/2019

INTRODUÇÃO

A máquina de escrever do meu avô

Nem um Nobel escapa…

…da maldição do erro operacional

PS.: ele não é Nobel…

Homem foi a Lua há 50 anos

…e ainda…

Objetivos do curso

Meta final: que vários dos processos de análise de dados da empresa passem a ser feitos dentro do fluxo de trabalho do R.

Ao fim do curso o objetivo é que todos os fios da meada sejam puxados para que o aluno consiga continuar por si só usando a vasta documentação disponível.

Por que programar?

Também amo o Excel, mas amo mais as seguintes vantagens:

Por que programar em R?

Fluxo de trabalho

Exemplos de onde vamos chegar

É muito comum possuirmos dados gerados em planilhas ou em algum suporte de formato estruturado ou semi-estruturado.

Estes dados podem ser organizados de forma “tidy” para análise

Após a possível execução de modelos, podemos publicar os resultados.

Demo Planel

Impacto da temperatura no consumo de energia elétrica

Localização de empresas e dutos

Estimador de posição de fundos multimercado

Ambiente R/RStudio

R é uma linguagem que é interpretada por um engine gratuito.

RStudio é o melhor ambiente de programação da linguagem R. A versão mais simples, que é totalmente funcional, é gratuita.

Na visualização padrão, ele oferece um console para execução de comandos e uma janela com a visualização dos environments, ou seja, das variáveis que ele guarda na sessão atual.

RStudio como console

No console é possível executar comandos, como o que atribui valor a uma variável

x <- 1

Note que a atribuição é feita com <- e não com = como na maioria das linguagens.

Dica: o atalho alt + - gera o sinal de atribuição

Os comandos que não atribuem valor a uma variável são ecoados na tela

x + 2
## [1] 3

Veja o [1] no console. O R considera que tudo é um vetor. É uma linguagem muito baseada em operações vetoriais. Isso facilita muito as coisas quando se lida com dados.

RStudio como IDE para um script

O console serve só para testes, aprendizado de novos comandos, debug, experiências etc.

Para as atividades mais comuns de análise de dados, e para que elas sejam reprodutíveis, é necessária a criação de scripts.

Eles são salvos em um arquivo de extensão “.r”

Funcionalidades interessantes do RStudio

Baixando o material do github

Todo o material do curso está hospedado no Github, inclusive esta apresentação, escrita em RMarkdown.

Os exemplos de código, as imagens e os dados mostrados nesta apresentação estão inclusos no repositório do curso.

O repositório fica em github/crotman/cursoR.

Para baixar este repositório no RStudio, crie um projeto em File/New Project, do tipo Github e use o endereço do repositório: https://github.com/crotman/CursoR.git.

Todo material é disponibilizado sob a licença Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License

FUNDAMENTOS DA LINGUAGEM

Tipos de valores “armazenados” por variáveis

Para o R, simplificando para o escopo deste curso, as variáveis “armazenam” os seguintes tipos:

1L:10L
##  [1]  1  2  3  4  5  6  7  8  9 10
list("oi", 1L)
## [[1]]
## [1] "oi"
## 
## [[2]]
## [1] 1
tibble(col1 = 1:10, col2 = 11:20 )
## # A tibble: 10 x 2
##     col1  col2
##    <int> <int>
##  1     1    11
##  2     2    12
##  3     3    13
##  4     4    14
##  5     5    15
##  6     6    16
##  7     7    17
##  8     8    18
##  9     9    19
## 10    10    20

Tipos de valores “armazenados” por variáveis (cont.)

f <- function(a, b){
    a + b
}

g <- f

g(1L, 2L)
## [1] 3
e1 <- rlang::env(
    a = 1L,
    b = "sou o b",
    c = 1L:20L
)

get("b", e1)
## [1] "sou o b"

Tipos de valores “armazenados” por variáveis (cont.)

Existe orientação a objetos no R, mas não está no escopo deste curso

Note que não há variáveis que armazenam dado escalar, como já vimos.

Dentre os vetores há:

Tipos de vetores

Fonte: Advanced R

Tipos de valores “armazenados” por variáveis (cont.)

Os vetores atômicos podem ser dos seguintes tipos:

Tipos primários

Fonte: Advanced R

Tipos de vetores atômicos

booleano <- !TRUE 

booleano
## [1] FALSE
inteiro <- 8L 

typeof(inteiro + 1L)
## [1] "integer"
typeof(inteiro + 1)
## [1] "double"

Tipos de vetores atômicos (cont.)

double <- 0.1

double_cientifico <- 1.5e3

infinito <- Inf 
double
## [1] 0.1
double_cientifico
## [1] 1500
infinito
## [1] Inf

Cobinando vetores em vetores maiores usando c()

Uma das funções mais usadas do R é c(), que cria um vetor novo vetor combinando vetores.

c(1, 2, 3)
## [1] 1 2 3
c(1, 2, 3, c(4, 5, 6))
## [1] 1 2 3 4 5 6
1.4 : 9.4
## [1] 1.4 2.4 3.4 4.4 5.4 6.4 7.4 8.4 9.4

Outras formas de gerar um vetor

O operador : é usado para gerar um vetor com todos números que estão entre os operandos e são formados somando números inteiros ao primeiro operando.

1L:10L
##  [1]  1  2  3  4  5  6  7  8  9 10
1.5:9.1
## [1] 1.5 2.5 3.5 4.5 5.5 6.5 7.5 8.5

A função seq() é usada para criar um vetor de várias formas.

Numa das formas especifica-se o valor inicial, o valor final e o incremento entre elementos do vetor.

seq(1, 9.99, 0.1)
##  [1] 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.1 2.2 2.3 2.4 2.5 2.6
## [18] 2.7 2.8 2.9 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 4.0 4.1 4.2 4.3
## [35] 4.4 4.5 4.6 4.7 4.8 4.9 5.0 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 6.0
## [52] 6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 7.0 7.1 7.2 7.3 7.4 7.5 7.6 7.7
## [69] 7.8 7.9 8.0 8.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8 8.9 9.0 9.1 9.2 9.3 9.4
## [86] 9.5 9.6 9.7 9.8 9.9

Parâmetros nomeados

Note que chamamos a função passando os parâmetros sem especificação de quais são eles. Eles são recebidos pela função dem específica.

Mas no R também é possível passar parâmetros de forma nomeada.

Clique em F1 enquanto tem o cursor em cima da função e veja a ordem dos parâmetros. Veja que outros parâmetros que não utilizamos. Podemos usar length.out ao invés de by:

seq(1L, 10L, length.out = 10L)
##  [1]  1  2  3  4  5  6  7  8  9 10
seq(1L, 10L, length.out = 5L)
## [1]  1.00  3.25  5.50  7.75 10.00

Outro parâmetro, along.with, deixa que criemos um vetor num intervalo determinado e o mesmo número de elementos do vetor passado por este parâmetro.

seq(20, 100, along.with = 1:10)
##  [1]  20.00000  28.88889  37.77778  46.66667  55.55556  64.44444  73.33333
##  [8]  82.22222  91.11111 100.00000

Valores faltantes NA

Valores faltantes ou desconecidos são representados por NA

a <- c(1L,NA)
a
## [1]  1 NA

O valor NA quase sempre contamina os cálculos

media <- mean(a)
media
## [1] NA

mas…

media <- mean(a, na.rm = TRUE)
media
## [1] 1

A exceção são expressões que dão sempre o mesmo resultado independentemente do valor da variável

NA ^ 0
## [1] 1
NA | TRUE
## [1] TRUE
NA & FALSE
## [1] FALSE

A melhor forma de testar se existe um valor NA é is.na

v <- c(1, NA, 2)

is.na(v)
## [1] FALSE  TRUE FALSE

Programação com vetores

As operações do R são vetoriais. Numa operação entre um vetor e um escalar, a operação com o escalar é aplicada a cada elemento do vetor

1:5 * 2
## [1]  2  4  6  8 10
1:10 / 10
##  [1] 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

Numa operação com vetores do mesmo tamanho, os elementos são pareados

1:10 * 1:10
##  [1]   1   4   9  16  25  36  49  64  81 100

Programação com vetores - recycling

Outro conceito importante é o de recycling.

Numa operação entre dois vetores de tamanhos diferentes, o vetor menor é repetido ciclicamente de forma a ficar com o mesmo tamanho do vetor maior.

Lembra que toda variável no R é um vetor?

Então… o escalar mostrado no primeiro código do slide anterior é um vetor de 1 elemento que sofre recycling

1:10 * 1:2
##  [1]  1  4  3  8  5 12  7 16  9 20

Estruturas construídas a partir de vetores e listas

Existem estruturas mais complexas na linguagem construídas a partir de vetores e listas.

Vamos passar pelo Data Frame agora. Depois por Factor e objetos que representam Datas

Data Frames

Data Frames, e seu primo Tibble, são estruturas muito usadas em análises de dados feitas em R.

O dataframe consiste em um conjunto de vetores nomeados, com o mesmo número de elementos, que formam uma estrutura retangular, onde cada coluna é um vetor e cada linha n contém o n-ésimo elemento dos vetores.

É similar, em muitas características, a uma tabela de banco de dados.

Essa estrutura é chave no paradigma “Tidy” que usaremos com as bibliotecas Tidyverse

Tibble é uma adaptação do Data Frame para análise de dados. Discutir essas diferenças está fora do escopo do curso. Algumas diferenças serão citadas o longo do material e justificam o uso do Tibble.

df <- 
    data.frame(
        nome = c("João", "Maria", "Zezinho", "Juquinha"), 
        idade = c(7, 8, 9, 10), 
        altura = c(10, 11)
    )
df
##       nome idade altura
## 1     João     7     10
## 2    Maria     8     11
## 3  Zezinho     9     10
## 4 Juquinha    10     11
#tibble não aceita recycling em vetores de tamanho diferente de 1
tib <- 
    #try evita que o erro paralise toda a execução do script
    try(
        tibble(
            nome = c("João", "Maria", "Zezinho", "Juquinha"), 
            idade = c(7, 8, 9, 10), 
            altura = c(10, 11)
        )
    )
## Error : Tibble columns must have consistent lengths, only values of length one are recycled:
## * Length 2: Column `altura`
## * Length 4: Columns `nome`, `idade`
## Backtrace:
##      x
##   1. +-rmarkdown::render("C:/dataquant/novo/CursoR/Conteudo.Rmd", encoding = "UTF-8")
##   2. | \-knitr::knit(...)
##   3. |   \-knitr:::process_file(text, output)
##   4. |     +-base::withCallingHandlers(...)
##   5. |     +-knitr:::process_group(group)
##   6. |     \-knitr:::process_group.block(group)
##   7. |       \-knitr:::call_block(x)
##   8. |         \-knitr:::block_exec(params)
##   9. |           +-knitr:::in_dir(...)
##  10. |           \-knitr:::evaluate(...)
##  11. |             \-evaluate::evaluate(...)
##  12. |               \-evaluate:::evaluate_call(...)
##  13. |                 +-evaluate:::timing_fn(...)
##  14. |                 +-base:::handle(...)
##  15. |                 +-base::withCallingHandlers(...)
##  16. |                 +-base::withVisible(eval(expr, envir, enclos))
##  17. |                 \-base::eval(expr, envir, enclos)
##  18. |                   \-base::eval(expr, envir, enclos)
##  19. +-base::try(...)
##  20. | \-base::tryCatch(...)
##  21. |   \-base:::tryCatchList(expr, classes, parentenv, handlers)
##  22. |     \-base:::tryCatchOne(expr, names, parentenv, handlers[[1L]])
##  23. |       \-base:::doTryCatch(return(expr), name, parentenv, handler)
##  24. \-tibble::tibble(...)
##  25.   \-tibble:::lst_to_tibble(xlq$output, .rows, .name_repair, lengths = xlq$lengths)
##  26.     \-tibble:::recycle_columns(x, .rows, lengths)

Controle de fluxo

A linguagem oferece comandos de controle de fluxo similares aos de outras linguagens.

Podemos dividir os comandos de controle de fluxo em dois tipos:

Choices: if, ifelse

O comando if funciona para um valor lógico escalar

if (2 + 2 == 4) {
    "2 mais 2 são 4"
} else {
    "2 mais 2 não são 4"
}
## [1] "2 mais 2 são 4"

Note o operador de comparação == e não =

A função if_else (da biblioteca dplyr) funciona de vetorial. if_else é mais rápida que a função ifelse da biblioteca base, mas só aceita argumentos de mesmo tipo no segundo e terceiro parâmetros

jogo_do_pim_silvio_santos <- if_else(
    condition = 1:40 %% 4 == 0 ,
    true =  "PIM",
    false =  as.character(1:40)
)
jogo_do_pim_silvio_santos
##  [1] "1"   "2"   "3"   "PIM" "5"   "6"   "7"   "PIM" "9"   "10"  "11" 
## [12] "PIM" "13"  "14"  "15"  "PIM" "17"  "18"  "19"  "PIM" "21"  "22" 
## [23] "23"  "PIM" "25"  "26"  "27"  "PIM" "29"  "30"  "31"  "PIM" "33" 
## [34] "34"  "35"  "PIM" "37"  "38"  "39"  "PIM"

Note o operador %% e a função de coerção de tipo as.character

Choices: switch e case_when

A cláusula switch e a função dplyr::case_when evitam que o programador tenha que criar muitos if else aninhados

letra <- "b"

switch(
    letra,
    "a" = "começa com a",
    "b" = "começa com b",
    stop("deu ruim")
)
## [1] "começa com b"

Note que a condição vai sendo testada na ordem e stop gera um erro

case_when serve ao caso vetorial

case_when(
    1:40 %% 10 == 0 ~ "dezena",
    1:40 %% 2 == 0 ~ "par",
    TRUE ~ as.character(1:40)
)
##  [1] "1"      "par"    "3"      "par"    "5"      "par"    "7"     
##  [8] "par"    "9"      "dezena" "11"     "par"    "13"     "par"   
## [15] "15"     "par"    "17"     "par"    "19"     "dezena" "21"    
## [22] "par"    "23"     "par"    "25"     "par"    "27"     "par"   
## [29] "29"     "dezena" "31"     "par"    "33"     "par"    "35"    
## [36] "par"    "37"     "par"    "39"     "dezena"

Loops

A cláusula de loop mais usada e mais versátil é for

for(i in 1:5){ 
    print(i^2)
}
## [1] 1
## [1] 4
## [1] 9
## [1] 16
## [1] 25

As cláusulas next e break modificam o comportamento, respectivamente caminhando direto para a próxima iteração e saindo do for

#next vai pra próxima iteração
for(i in 1:5){
    if (i %% 2 == 0){
        next
    }
    print(i)
}
## [1] 1
## [1] 3
## [1] 5
#next sai do loop
for(i in 1:5){
    if (i %% 2 == 0){
        break
    }
    print(i)
}
## [1] 1

Loops: coisa do passado

Vamos ver que quase sempre é desnecessário usar loop para as tarefas que vamos executar.

O caráter vetorial da linguagem, aliado a funcionalidades das bibliotecas, faz com que a grande maioria dos loops sejam desnecessários.

O código fica mais limpo e expressivo e mais rápido. Às vezes MUITO mais rápido. Isso ocorre por motivos além do escopo do curso (alocação de memória, código interpretado x código compilado em C++ etc.)

O código abaixo usa loop e programação funcional, respectivamente. Programação funcional será abordada posteriormente no material.

com_loop <- function(n){
    x <- integer()
    for (i in 1:n){
        x <- c(x, i^2)
    }
    x
}

#programação funcional: aprenderemos posteriomente
sem_loop <- function(n){
    x <- 1:n %>% 
        map_dbl(function(x){x^2})
    x
}

Abaixo as três formas de fazer a mesma conta que terão a performance avaliada

com_loop(5)
## [1]  1  4  9 16 25
sem_loop(5)
## [1]  1  4  9 16 25
(1:5)^2
## [1]  1  4  9 16 25

Loops: coisa do passado (cont.)

A biblioteca bench oferece funções ótimas para avaliar a performance de pedaços pequenos de código.

resultados_perf <- mark(
    sem_loop(1e4),
    com_loop(1e4),
    (1:1e4)^2
)

#aprenderemos o que é %>% e select() posteriormente 
resultados_perf %>% 
    select(expression, min, median, `itr/sec` )
## # A tibble: 3 x 4
##   expression           min   median `itr/sec`
##   <bch:expr>      <bch:tm> <bch:tm>     <dbl>
## 1 sem_loop(10000)   6.45ms   6.97ms    130.  
## 2 com_loop(10000)  92.95ms  113.7ms      7.29
## 3 (1:10000)^2         16us   17.3us  22478.
plot(resultados_perf)

Revisão: tipos de vetores

Revisão: operações vetoriais

Qual o valor de v1 ?

v1 <- c(1, 2, 3, 4, 5, 6) * 2

Revisão: operações vetoriais

Qual o valor de v2

v2 <-  c(1, 2, 3, 4) + c(0, 1)

Revisão: data frames

Revisão: controle de fluxo

Revisão: if

Complete a lacuna:

if (n_pessoas ____ 0 ){
    print("Não há pessoas")
}
else{
    print("Há pessoas")
}

Revisão: if_else

Complete a lacuna:

x <- 1:10

if_else(
    _________,
    "par",
    "impar"
)

Revisão: if_else

Qual o valor de x ?

times <- c("Flamengo", "Fluminense", "Bahia", "Vasco")

x <- case_when(
    times == "Flamengo" ~ "Flamengo",
    times == "Bahia" ~ "Bahia",
    TRUE ~ "Flumifogo da Gama"
)

Revisão: for

Complete a lacuna para imprimir os quadrados dos números de 1 a 10

for ____{
    print(x^2)
}

Exemplo de simulação: Monty Hall

Monty Hall era uma espécie de Sílvio Santos juvenil (sub 80) americano.

Um dos seus jogos consistia em mostrar três portas ao otár… (ops) convidado. Em uma delas tem um carro.

Antes do resultado, o apresentador revela uma das portas e pergunta se o convidado que trocar a escolha.

O que vocês acham? Melhor trocar, manter a escolha original ou tanto faz?

Simulando o Monty Hall

Note o que há de interessante no código (comentado)

set.seed(88)

joga_monty_hall <- function(troca){
    portas <- 1:3
    #sample() sorteia elementos com ou sem reposição
    porta_carro <- sample(portas, size = 1, replace = FALSE)
    primeira_escolha <- 1
    #Seleção negativa (retirando elementos)
    portas_pra_revelar <- portas[-c(porta_carro, primeira_escolha)]
    porta_revelada <- sample( c(portas_pra_revelar, portas_pra_revelar  ), 1)

    if(troca){
        escolha <- portas[-c(primeira_escolha, porta_revelada)]
    }
    else{
        escolha <- primeira_escolha
    }
    
    escolha == porta_carro
        
}

n <- 1000
#replicate executa múltiplas vezes um comando e armazena os resultados em uma estruturaúnica
troca <- replicate(n = n, joga_monty_hall(troca = TRUE))
fica  <- replicate(n = n, joga_monty_hall(troca = FALSE))

Resultados:

sum(troca)/n
## [1] 0.675
sum(fica)/n
## [1] 0.323

Outra simulação: dá pra passar no CFA sem saber nada?

Vamos ver… mas dá pra simular sem saber quase nada.

Vamos usar uma das funções da família r<familia de distribuição de prob>(). Neste caso, a rbinom, que simula a distribução binomial (aquela que equivale ao evento de jogar n moedas (ou alguma coisa com dois lados) para cima e ver quantas deram cara).

n_simul <- 10000
n_questoes <- 240
min_aprovacao <-  0.6
n_aprovado <- 240 * min_aprovacao
prob_questao <- 0.2

acertos <- rbinom(n = n_simul, size = n_questoes, prob = prob_questao   )

sum(acertos >= n_aprovado)/n_simul 
## [1] 0

A chance é praticamente nula.

Na verdade, a grande massa da distribuição fica muito distante.

dado <- enframe(acertos/n_questoes)

mostra_chances <- function(acertos, n_questoes){
    ggplot(enframe(acertos/n_questoes)) +
        geom_density( aes(x = value)) +
        scale_x_continuous(
            labels = percent_format(accuracy = 1), 
            limits = c(0,1),
            breaks = seq(0, 1, 0.1) 
            ) +
        labs(x ="% Acertos") +
        geom_vline(xintercept = min_aprovacao, color = "red") +
        theme_light()
}

mostra_chances(acertos, n_questoes)

Outra simulação: dá pra passar no CFA sabendo a um grau x ?

O exemplo anterior era muito simplista: ninguém chuta tudo.

Imagine que sabemos qual a chance de aparecer uma pergunta onde podemos descartar 0 alternativas, a chance de uma onde descartamos 1 e assim por diante.

#definindo a chance podermos eliminar 0, 1, 2, ... 4 alternativas
fracao_eliminar_questoes <- c( 0.1, 0.1, 0.2, 0.25 , 0.35 ) 
#definindo o número de questões 
n_questoes_cada_elimina <- t(rmultinom(n_simul, size = n_questoes, fracao_eliminar_questoes))
probs_quando_elimina <- 1/(5:1)
acertos_concatenados <- 
    rbinom( 
        n =  n_simul * 5 , 
        size = as.vector(t(n_questoes_cada_elimina)), 
        prob = probs_quando_elimina  
    )
n_questoes_cada_elimina[1:4,]
##      [,1] [,2] [,3] [,4] [,5]
## [1,]   23   24   50   47   96
## [2,]   24   17   48   64   87
## [3,]   23   30   43   56   88
## [4,]   20   25   51   52   92
acertos_concatenados[1:20]
##  [1]  4  6 17 25 96  4  3 13 34 87  6  5 13 36 88  4  9 17 25 92
matriz_acertos <- matrix(acertos_concatenados, byrow = TRUE, nrow = n_simul )

matriz_acertos[1:5,]
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    4    6   17   25   96
## [2,]    4    3   13   34   87
## [3,]    6    5   13   36   88
## [4,]    4    9   17   25   92
## [5,]    6    5   11   30   92
acertos <- rowSums(matriz_acertos)
sum(acertos > n_aprovado)/n_simul
## [1] 0.3131
mostra_chances(acertos, n_questoes)

Exemplo inicial de visualização de dados

library(ggplot2)

ggplot(data = mpg) + 
    geom_point(mapping = aes(x = displ, y = hwy))

TIDY DATA (obtenção e organização dos dados)

A habilidade mais subestimada

Dentro de todo o hype envolvendo Data Science, surgem as buzz words mais mirabolantes: machine learning, AI, Deep Learning…

Tudo isso é legal, mas a habilidade de preparar os dados para os modelos, preparar os hiperparâmetros e especificações alternativas ainda melhora muito a análise. Anote mais uma buzz word: FEATURE ENGINEERING

A agilidade de se tentar abordagens alterativas com os dados cresce muito quando dominamos a arte de manipular os dataframes. Por isso o peso grande dado neste curso.

Organizando os dados de forma tidy

Arrumar os dados de forma que as linhas sejam eventos e as colunas sejam atributos do evento ajuda muito a rodar modelos e construir visualizações eficientemente.

O que é o evento e o que é o atributo pode variar até para diferentes usos do mesmo dado. Mas a prática ajuda a determinar isso.

Tratamento de dados em passos: operador Pipe (%>%)

Normalmente os tratamentos de dados são feitos em múltiplos passos encadeados:

#dados de exemplo
head(gapminder)
## # A tibble: 6 x 6
##   country     continent  year lifeExp      pop gdpPercap
##   <fct>       <fct>     <int>   <dbl>    <int>     <dbl>
## 1 Afghanistan Asia       1952    28.8  8425333      779.
## 2 Afghanistan Asia       1957    30.3  9240934      821.
## 3 Afghanistan Asia       1962    32.0 10267083      853.
## 4 Afghanistan Asia       1967    34.0 11537966      836.
## 5 Afghanistan Asia       1972    36.1 13079460      740.
## 6 Afghanistan Asia       1977    38.4 14880372      786.

Vamos imaginar que queremos a média de PIB per capita por continente em 2007.

Note quanto código desnecessário há nestas linhas: variáveis que não precisavam ser nomeadas nem passadas explicitamente como parâmetro.

Este código desnecessário causa fadiga no programador e confunde o próprio programador e o leitor posterior do código.

#vamos cobrir essas funções de tratamento posteriormente
gapminder_07 <- filter(gapminder, year == 2007)
gapminder_07_group_continente <- group_by(gapminder_07, continent)
gapminder_media_gdp_continente <- summarise(
    gapminder_07_group_continente, media_gdp = sum(gdpPercap * pop)/sum(pop)
)
resultado <- arrange(gapminder_media_gdp_continente, desc(media_gdp))

resultado
## # A tibble: 5 x 2
##   continent media_gdp
##   <fct>         <dbl>
## 1 Oceania      32885.
## 2 Europe       25244.
## 3 Americas     21603.
## 4 Asia          5432.
## 5 Africa        2561.

Tratamento de dados em passos: operador Pipe (%>%) (cont.)

O operador pipe %>% faz o seguinte:

x %>% y(z) = y(x,z)

Ou seja, o primeiro operando é enfiado como primeiro parâmetro da função que está no segundo operando.

Isso faz com que possamos escrever o código anterior assim:

resultado <- gapminder %>% 
    filter(year == 2007) %>% 
    group_by(continent) %>% 
    summarise(
        media_gdp = sum(gdpPercap * pop) / sum(pop)
    ) %>% 
    arrange(desc(media_gdp))
    
resultado
## # A tibble: 5 x 2
##   continent media_gdp
##   <fct>         <dbl>
## 1 Oceania      32885.
## 2 Europe       25244.
## 3 Americas     21603.
## 4 Asia          5432.
## 5 Africa        2561.

Note que agora podemos interpretar o código facilmente como uma série de comandos de tratamento em cima dos dados.

Não é por coincidência que as funções de tratamento das bibliotecas tidyverse que veremos adiante são verbos e recebem os dados como primeiro parâmetro.

Agora o mais importante de tudo: O ATALHO PARA O %>% É CTRL + SHIFT + M

Um mapa conceitual da bibioteca de transformação de dados dplyr

CRAN: uma Disneylândia dos dados?

CRAN é o repositório de bibliotecas mantido pelo R com contribuição de populares.

Além de funcionalidades estatísticas e funcionalidades para lidar com dados, há dados e funcionalidades para buscar dados online.

Usaremos várias das bases como exemplo.

A primeira é a do Banco Mundial, muito rica para quem gosta de dados socioeconômicos

Para acessar um indicador precisamos achá-lo na base de indicadores com a função wbsearch()

#pattern é uma expressão regular. \\ serve para dizer que "(" é mesmo "(" 
#e não o ( usado nas operações de expressão regular (fora do escopo do curso)
indicadores <- wbsearch(pattern = "GINI index \\(World Bank estimate\\)")

indicadores
##      indicatorID                        indicator
## 1348 SI.POV.GINI GINI index (World Bank estimate)

Sabendo o ID do indicador, podemos consultá-lo com a função wb()

#mrv é most recent values. Pode ser usado para buscar os n valores mais recentes
gini = wb(indicator = "SI.POV.GINI", mrv= 10, POSIXct = TRUE)

head(gini)
##     iso3c date value indicatorID                        indicator iso2c
## 486   ALB 2012  29.0 SI.POV.GINI GINI index (World Bank estimate)    AL
## 490   ALB 2008  30.0 SI.POV.GINI GINI index (World Bank estimate)    AL
## 497   DZA 2011  27.6 SI.POV.GINI GINI index (World Bank estimate)    DZ
## 530   AGO 2008  42.7 SI.POV.GINI GINI index (World Bank estimate)    AO
## 541   ARG 2017  41.2 SI.POV.GINI GINI index (World Bank estimate)    AR
## 542   ARG 2016  42.0 SI.POV.GINI GINI index (World Bank estimate)    AR
##       country    date_ct granularity
## 486   Albania 2012-01-01      annual
## 490   Albania 2008-01-01      annual
## 497   Algeria 2011-01-01      annual
## 530    Angola 2008-01-01      annual
## 541 Argentina 2017-01-01      annual
## 542 Argentina 2016-01-01      annual

dplyr: Modificar -> Colunas

Funções básicas de tratamento (dplyr): select()

A função select() é usada para selecionar colunas do dataframe/tibble

glimpse(gini)
## Observations: 682
## Variables: 9
## $ iso3c       <chr> "ALB", "ALB", "DZA", "AGO", "ARG", "ARG", "ARG", "...
## $ date        <chr> "2012", "2008", "2011", "2008", "2017", "2016", "2...
## $ value       <dbl> 29.0, 30.0, 27.6, 42.7, 41.2, 42.0, 41.7, 41.0, 41...
## $ indicatorID <chr> "SI.POV.GINI", "SI.POV.GINI", "SI.POV.GINI", "SI.P...
## $ indicator   <chr> "GINI index (World Bank estimate)", "GINI index (W...
## $ iso2c       <chr> "AL", "AL", "DZ", "AO", "AR", "AR", "AR", "AR", "A...
## $ country     <chr> "Albania", "Albania", "Algeria", "Angola", "Argent...
## $ date_ct     <date> 2012-01-01, 2008-01-01, 2011-01-01, 2008-01-01, 2...
## $ granularity <chr> "annual", "annual", "annual", "annual", "annual", ...
gini_select <- gini %>% 
    select(country, date, value, iso3c)

head(gini_select)
##       country date value iso3c
## 486   Albania 2012  29.0   ALB
## 490   Albania 2008  30.0   ALB
## 497   Algeria 2011  27.6   DZA
## 530    Angola 2008  42.7   AGO
## 541 Argentina 2017  41.2   ARG
## 542 Argentina 2016  42.0   ARG

É possível usar a seleção negativa assim como fizemos com vetores

gini_select2 <- gini_select %>% 
    select(-iso3c)

head(gini_select2)
##       country date value
## 486   Albania 2012  29.0
## 490   Albania 2008  30.0
## 497   Algeria 2011  27.6
## 530    Angola 2008  42.7
## 541 Argentina 2017  41.2
## 542 Argentina 2016  42.0

Funções básicas de tratamento (dplyr): select() : helpers

Funções básicas de tratamento (dplyr): select() (cont.)

Algumas funções helpers nos ajudam a usar a função select e são muito úteis para tratamentos mais elaborados.

Pra mostrar mais funcionalidades da função select, vamos usar uma base com dados eleitorais brasileiros, que retorna mais colunas

candidatos <- candidate_fed(2018)
## Processing the data...
## Done.
glimpse(candidatos)
## Observations: 29,113
## Variables: 58
## $ DATA_GERACAO                   <chr> "15/11/2018", "15/11/2018", "15...
## $ HORA_GERACAO                   <time> 20:03:47, 20:03:47, 20:03:47, ...
## $ ANO_ELEICAO                    <dbl> 2018, 2018, 2018, 2018, 2018, 2...
## $ COD_TIPO_ELEICAO               <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2...
## $ NOME_TIPO_ELEICAO              <chr> "ELEIÇÃO ORDINÁRIA", "ELEIÇÃO O...
## $ NUM_TURNO                      <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1...
## $ COD_ELEICAO                    <dbl> 297, 297, 297, 297, 297, 297, 2...
## $ DESCRICAO_ELEICAO              <chr> "Eleições Gerais Estaduais 2018...
## $ DATA_ELEICAO                   <chr> "07/10/2018", "07/10/2018", "07...
## $ ABRANGENCIA                    <chr> "ESTADUAL", "ESTADUAL", "ESTADU...
## $ SIGLA_UF                       <chr> "AC", "AC", "AC", "AC", "AC", "...
## $ SIGLA_UE                       <chr> "AC", "AC", "AC", "AC", "AC", "...
## $ DESCRICAO_UE                   <chr> "ACRE", "ACRE", "ACRE", "ACRE",...
## $ CODIGO_CARGO                   <dbl> 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7...
## $ DESCRICAO_CARGO                <chr> "DEPUTADO ESTADUAL", "DEPUTADO ...
## $ SEQUENCIAL_CANDIDATO           <dbl> 10000601020, 10000603904, 10000...
## $ NUMERO_CANDIDATO               <dbl> 14088, 31100, 45456, 35193, 173...
## $ NOME_CANDIDATO                 <chr> "ANA ISLA ARRUDA FREITAS", "MAR...
## $ NOME_URNA_CANDIDATO            <chr> "ANA FREITAS", "LORA DO COMERCI...
## $ NOME_SOCIAL_CANDIDATO          <chr> "#NULO#", "#NULO#", "#NULO#", "...
## $ CPF_CANDIDATO                  <chr> "93353405291", "63049112204", "...
## $ EMAIL_CANDIDATO                <chr> "ANAFREITAS.AIF@GMAIL.COM", "JU...
## $ COD_SITUACAO_CANDIDATURA       <dbl> 12, 12, 12, 12, 12, 12, 12, 12,...
## $ DES_SITUACAO_CANDIDATURA       <chr> "APTO", "APTO", "APTO", "APTO",...
## $ COD_DETALHE_SITUACAO_CAND      <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2...
## $ DES_DETALHE_SITUACAO_CAND      <chr> "DEFERIDO", "DEFERIDO", "DEFERI...
## $ TIPO_AGREMIACAO                <chr> "PARTIDO ISOLADO", "COLIGAÇÃO",...
## $ NUMERO_PARTIDO                 <dbl> 14, 31, 45, 35, 17, 27, 13, 13,...
## $ SIGLA_PARTIDO                  <chr> "PTB", "PHS", "PSDB", "PMB", "P...
## $ NOME_PARTIDO                   <chr> "PARTIDO TRABALHISTA BRASILEIRO...
## $ CODIGO_LEGENDA                 <dbl> 10000050036, 10000050109, 10000...
## $ NOME_COLIGACAO                 <chr> "PARTIDO ISOLADO", "FORÇA DA UN...
## $ COMPOSICAO_LEGENDA             <chr> "PTB", "PMB / PHS", "PSDB / DEM...
## $ CODIGO_NACIONALIDADE           <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1...
## $ DESCRICAO_NACIONALIDADE        <chr> "BRASILEIRA NATA", "BRASILEIRA ...
## $ SIGLA_UF_NASCIMENTO            <chr> "AC", "AC", "AC", "AC", "AC", "...
## $ CODIGO_MUNICIPIO_NASCIMENTO    <dbl> -3, -3, -3, -3, -3, -3, -3, -3,...
## $ NOME_MUNICIPIO_NASCIMENTO      <chr> "RIO BRANCO", "TARAUCÁ", "FEIJÓ...
## $ DATA_NASCIMENTO                <chr> "12/08/1987", "10/02/1976", "12...
## $ IDADE_DATA_POSSE               <dbl> 31, 42, 44, 47, 31, 26, 38, 39,...
## $ NUM_TITULO_ELEITORAL_CANDIDATO <chr> "005491592429", "001633372461",...
## $ CODIGO_SEXO                    <dbl> 4, 4, 4, 2, 2, 2, 2, 2, 2, 4, 2...
## $ DESCRICAO_SEXO                 <chr> "FEMININO", "FEMININO", "FEMINI...
## $ COD_GRAU_INSTRUCAO             <dbl> 4, 3, 6, 8, 7, 6, 8, 8, 7, 8, 8...
## $ DESCRICAO_GRAU_INSTRUCAO       <chr> "ENSINO FUNDAMENTAL COMPLETO", ...
## $ CODIGO_ESTADO_CIVIL            <dbl> 1, 1, 3, 3, 3, 1, 3, 3, 1, 3, 3...
## $ DESCRICAO_ESTADO_CIVIL         <chr> "SOLTEIRO(A)", "SOLTEIRO(A)", "...
## $ CODIGO_COR_RACA                <chr> "03", "01", "01", "03", "03", "...
## $ DESCRICAO_COR_RACA             <chr> "PARDA", "BRANCA", "BRANCA", "P...
## $ CODIGO_OCUPACAO                <dbl> 581, 169, 999, 297, 999, 999, 2...
## $ DESCRICAO_OCUPACAO             <chr> "DONA DE CASA", "COMERCIANTE", ...
## $ DESPESA_MAX_CAMPANHA           <dbl> 0, -1, 0, -1, 0, 0, 0, 0, -1, 0...
## $ COD_SIT_TOT_TURNO              <dbl> 5, 5, 5, 5, 5, 4, 3, 5, 5, 4, 2...
## $ DESC_SIT_TOT_TURNO             <chr> "SUPLENTE", "SUPLENTE", "SUPLEN...
## $ SITUACAO_REELEICAO             <chr> "N", "N", "N", "N", "N", "N", "...
## $ SITUACAO_DECLARAR_BENS         <chr> "N", "N", "S", "S", "N", "N", "...
## $ NUMERO_PROTOCOLO_CANDIDATURA   <dbl> -1, -1, -1, -1, -1, -1, -1, -1,...
## $ NUMERO_PROCESSO                <chr> "06002996220186010000", "060045...

Funções básicas de tratamento (dplyr): select() - helpers

candidatos_select <- candidatos %>% 
    select(starts_with("NOME"))

datatable(head(candidatos_select))
candidatos_select <- candidatos %>% 
    select(ends_with("candidato"))

datatable(head(candidatos_select))
candidatos_select <- candidatos %>% 
    select(contains("municipio"))

datatable(head(candidatos_select))
candidatos_select <- candidatos %>% 
    select(ends_with("candidato"))

datatable(head(candidatos_select))

Funções básicas de tratamento (dplyr): select() - helpers (cont.)

A função helper num_range ajuda a encontrar colunas do tipo prefixo_n. Isso é muito comum em bases de dados

A biblioteca worldmet retorna dados de estações meteorológicas espalhadas pelo planeta

Primeiro é necessário encontrar o código da base desejada

estacao <- getMeta("heathrow", returnMap = TRUE)

estacao

Funções básicas de tratamento (dplyr): select() - helpers (cont.)

A função abaixo retorna os dados de uma estação. Veja que alguns campos têm um sufixo _n

dados_heathrow <- importNOAA(code = "037720-99999", year = 2019,
precip = TRUE, PWC = FALSE, parallel = TRUE)


glimpse(dados_heathrow)
## Observations: 6,166
## Variables: 26
## $ date        <dttm> 2019-01-01 00:00:00, 2019-01-01 01:00:00, 2019-01...
## $ usaf        <chr> "037720", "037720", "037720", "037720", "037720", ...
## $ wban        <chr> "99999", "99999", "99999", "99999", "99999", "9999...
## $ code        <chr> "037720-99999", "037720-99999", "037720-99999", "0...
## $ station     <chr> "HEATHROW", "HEATHROW", "HEATHROW", "HEATHROW", "H...
## $ lat         <dbl> 51.47967, 51.47967, 51.47967, 51.47967, 51.47967, ...
## $ lon         <dbl> -0.4573333, -0.4573333, -0.4573333, -0.4573333, -0...
## $ elev        <dbl> 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25...
## $ wd          <dbl> 283.706052, 286.994553, 290.000000, 290.000000, 27...
## $ ws          <dbl> 3.266667, 3.433333, 4.100000, 3.433333, 3.266667, ...
## $ ceil_hgt    <dbl> 657.3333, 667.3333, 728.0000, 768.0000, 778.3333, ...
## $ visibility  <dbl> 22000, 28000, 45000, 45000, 35000, 35000, 28000, 2...
## $ air_temp    <dbl> 8.666667, 8.933333, 9.000000, 9.000000, 7.966667, ...
## $ dew_point   <dbl> 4.9333333, 4.9666667, 4.7666667, 4.8000000, 4.8000...
## $ atmos_pres  <dbl> 1034.8, 1034.5, 1034.6, 1034.5, 1034.4, 1033.9, 10...
## $ RH          <dbl> 77.67802, 76.42835, 75.06237, 75.23079, 80.81974, ...
## $ cl_1        <dbl> 7.666667, 8.000000, 7.666667, 8.000000, 7.666667, ...
## $ cl_1_height <dbl> 657.3333, 667.3333, 728.0000, 768.0000, 778.3333, ...
## $ cl_2        <dbl> 8.000000, NA, 8.000000, NA, NA, NA, 7.000000, 7.00...
## $ cl_2_height <dbl> 792, NA, 914, NA, NA, NA, 1006, 1036, NA, 720, 121...
## $ cl_3        <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ cl_3_height <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ precip_12   <dbl> NA, NA, NA, NA, NA, NA, 0, NA, NA, NA, NA, NA, NA,...
## $ precip_6    <dbl> 0, NA, NA, NA, NA, NA, 0, NA, NA, NA, NA, NA, 0, N...
## $ cl          <dbl> 8.000000, 8.000000, 8.000000, 8.000000, 7.666667, ...
## $ precip      <dbl> NA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...

A função helper num_range ajuda a selecionar essas colunas com prefixo comum e um sufixo numérico

dados_heathrow_select <- dados_heathrow %>% 
    select( 
        date, 
        num_range("cl_", 1:3 ), 
        num_range("precip_", c(6, 12))  
    )


head(dados_heathrow_select)
## # A tibble: 6 x 6
##   date                 cl_1  cl_2  cl_3 precip_6 precip_12
##   <dttm>              <dbl> <dbl> <dbl>    <dbl>     <dbl>
## 1 2019-01-01 00:00:00  7.67     8    NA        0        NA
## 2 2019-01-01 01:00:00  8       NA    NA       NA        NA
## 3 2019-01-01 02:00:00  7.67     8    NA       NA        NA
## 4 2019-01-01 03:00:00  8       NA    NA       NA        NA
## 5 2019-01-01 04:00:00  7.67    NA    NA       NA        NA
## 6 2019-01-01 05:00:00  6       NA    NA       NA        NA

Outra função útil é a everything, que ajuda, por exemplo, a passar algumas colunas para o início do tibble.

dados_heathrow_select <- dados_heathrow %>% 
    select( 
        date, 
        air_temp,
        everything() 
    )


glimpse(dados_heathrow_select)
## Observations: 6,166
## Variables: 26
## $ date        <dttm> 2019-01-01 00:00:00, 2019-01-01 01:00:00, 2019-01...
## $ air_temp    <dbl> 8.666667, 8.933333, 9.000000, 9.000000, 7.966667, ...
## $ usaf        <chr> "037720", "037720", "037720", "037720", "037720", ...
## $ wban        <chr> "99999", "99999", "99999", "99999", "99999", "9999...
## $ code        <chr> "037720-99999", "037720-99999", "037720-99999", "0...
## $ station     <chr> "HEATHROW", "HEATHROW", "HEATHROW", "HEATHROW", "H...
## $ lat         <dbl> 51.47967, 51.47967, 51.47967, 51.47967, 51.47967, ...
## $ lon         <dbl> -0.4573333, -0.4573333, -0.4573333, -0.4573333, -0...
## $ elev        <dbl> 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25...
## $ wd          <dbl> 283.706052, 286.994553, 290.000000, 290.000000, 27...
## $ ws          <dbl> 3.266667, 3.433333, 4.100000, 3.433333, 3.266667, ...
## $ ceil_hgt    <dbl> 657.3333, 667.3333, 728.0000, 768.0000, 778.3333, ...
## $ visibility  <dbl> 22000, 28000, 45000, 45000, 35000, 35000, 28000, 2...
## $ dew_point   <dbl> 4.9333333, 4.9666667, 4.7666667, 4.8000000, 4.8000...
## $ atmos_pres  <dbl> 1034.8, 1034.5, 1034.6, 1034.5, 1034.4, 1033.9, 10...
## $ RH          <dbl> 77.67802, 76.42835, 75.06237, 75.23079, 80.81974, ...
## $ cl_1        <dbl> 7.666667, 8.000000, 7.666667, 8.000000, 7.666667, ...
## $ cl_1_height <dbl> 657.3333, 667.3333, 728.0000, 768.0000, 778.3333, ...
## $ cl_2        <dbl> 8.000000, NA, 8.000000, NA, NA, NA, 7.000000, 7.00...
## $ cl_2_height <dbl> 792, NA, 914, NA, NA, NA, 1006, 1036, NA, 720, 121...
## $ cl_3        <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ cl_3_height <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ precip_12   <dbl> NA, NA, NA, NA, NA, NA, 0, NA, NA, NA, NA, NA, NA,...
## $ precip_6    <dbl> 0, NA, NA, NA, NA, NA, 0, NA, NA, NA, NA, NA, 0, N...
## $ cl          <dbl> 8.000000, 8.000000, 8.000000, 8.000000, 7.666667, ...
## $ precip      <dbl> NA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...

Funções básicas de tratamento (dplyr): mutate()

A função mutate é usada para criar novas colunas no tibble

Notando que a coluna DATA_ELEICAO é um caracter, vamos criar uma coluna de tipo data.

typeof(candidatos$DATA_ELEICAO)
## [1] "character"

O jeito mais fácil de fazer isso é usando uma das funções da biblioteca lubridate que veremos em detalhes em seguida

candidatos_com_data <- candidatos %>% 
    mutate(DATA_ELEICAO_TIPO_DATA = dmy(DATA_ELEICAO)) %>% 
    select(DATA_ELEICAO, DATA_ELEICAO_TIPO_DATA)

head(candidatos_com_data)
## # A tibble: 6 x 2
##   DATA_ELEICAO DATA_ELEICAO_TIPO_DATA
##   <chr>        <date>                
## 1 07/10/2018   2018-10-07            
## 2 07/10/2018   2018-10-07            
## 3 07/10/2018   2018-10-07            
## 4 07/10/2018   2018-10-07            
## 5 07/10/2018   2018-10-07            
## 6 07/10/2018   2018-10-07

É possível substituir a um campo existente

candidatos_com_data <- candidatos %>% 
    mutate(DATA_ELEICAO = dmy(DATA_ELEICAO)) %>% 
    select(DATA_ELEICAO)

head(candidatos_com_data)
## # A tibble: 6 x 1
##   DATA_ELEICAO
##   <date>      
## 1 2018-10-07  
## 2 2018-10-07  
## 3 2018-10-07  
## 4 2018-10-07  
## 5 2018-10-07  
## 6 2018-10-07

Funções básicas de tratamento (dplyr): mutate()

funções derivadas da mutate possibilitam a alteração de várias colunas ao mesmo tempo, usando os mesmos helpers que jã vimos para a select e uma função à escolha

candidatos_com_data <- candidatos %>% 
    mutate_at(vars(starts_with("DATA_")), dmy ) %>% 
    select(starts_with("DATA_"))


head(candidatos_com_data)
## # A tibble: 6 x 3
##   DATA_GERACAO DATA_ELEICAO DATA_NASCIMENTO
##   <date>       <date>       <date>         
## 1 2018-11-15   2018-10-07   1987-08-12     
## 2 2018-11-15   2018-10-07   1976-02-10     
## 3 2018-11-15   2018-10-07   1974-08-12     
## 4 2018-11-15   2018-10-07   1971-09-09     
## 5 2018-11-15   2018-10-07   1988-02-01     
## 6 2018-11-15   2018-10-07   1992-11-11

Funções básicas de tratamento (dplyr) mutate() (cont.):

Outras funções úteis são as que fazem operações acumuladas e as operações de lag() e lead()

series <- BETSsearch("exchange dollar")
## 
## BETS-package: Found 33 out of 18706 time series.
series
## # A tibble: 33 x 7
##    code  description           unit   periodicity start  last_value source 
##    <chr> <chr>                 <chr>  <chr>       <chr>  <chr>      <chr>  
##  1 1     Exchange rate - Free~ c.m.u~ D           28/11~ 26/03/2018 Sisbac~
##  2 10813 Exchange rate - Free~ c.m.u~ D           28/11~ 03/05/2018 Sisbac~
##  3 11753 Real effective excha~ Index  M           31/01~ mar/2018   BCB-DS~
##  4 11758 Real effective excha~ Index  M           31/01~ mar/2018   BCB-DS~
##  5 11763 Real effective excha~ Index  M           31/01~ mar/2018   BCB-DS~
##  6 11768 Real effective excha~ Index  M           31/01~ mar/2018   BCB-DS~
##  7 17887 Exchange rate (perio~ US$/c~ M           31/01~ nov/2016   IMF-IFS
##  8 17888 Exchange rate (perio~ US$/c~ M           31/01~ nov/2016   IMF-IFS
##  9 18441 Exchange rate (end o~ US$/c~ M           31/01~ nov/2016   IMF-IFS
## 10 18442 Exchange rate (end o~ US$/c~ M           31/01~ nov/2016   IMF-IFS
## # ... with 23 more rows

No código abaixo, calculamos o retorno da série, a volatilidade histórica e a volatilidade EWMA

dolar <- BETSget(1) 

dolar_com_vol <- dolar %>% 
    filter(date > ymd("1994-07-01")) %>% 
    arrange(date) %>% 
    mutate(
        retorno = (value - lag(value))/value,
        retorno_quad = retorno^2,
        dia = row_number(),
        fator_ewma = (1/0.94)^dia*1e-20
    ) %>% 
    filter(!is.na(retorno)) %>% 
    mutate(vol = sqrt(cumvar(retorno)) * sqrt(252) ) %>% 
    mutate(vol_ewma = sqrt(cumsum(retorno_quad * fator_ewma)/cumsum(fator_ewma)) * sqrt(252) ) %>% 
    rename(dolar = value) %>% 
    select(
        date,
        dolar,
        retorno,
        vol,
        vol_ewma
    )

datatable(dolar_com_vol) %>% 
    formatPercentage(c("retorno", "vol", "vol_ewma"), 2)
dolar_ajeitado <- dolar_com_vol %>% 
    gather(variavel, valor, - date)


dolar_ajeitado %>% 
    ggplot() +
    geom_line(aes(x = date, y = valor)) +
    facet_grid( variavel ~ . , scales = "free") +
    theme_light() 

Funções básicas de tratamento (dplyr) arrange():

A função arrange serve para ordenar o tibble.

dados_ordenados <- dados_heathrow %>% 
    arrange(date)

head(dados_ordenados)
## # A tibble: 6 x 26
##   date                usaf  wban  code  station   lat    lon  elev    wd
##   <dttm>              <chr> <chr> <chr> <chr>   <dbl>  <dbl> <dbl> <dbl>
## 1 2019-01-01 00:00:00 0377~ 99999 0377~ HEATHR~  51.5 -0.457    25  284.
## 2 2019-01-01 01:00:00 0377~ 99999 0377~ HEATHR~  51.5 -0.457    25  287.
## 3 2019-01-01 02:00:00 0377~ 99999 0377~ HEATHR~  51.5 -0.457    25  290 
## 4 2019-01-01 03:00:00 0377~ 99999 0377~ HEATHR~  51.5 -0.457    25  290 
## 5 2019-01-01 04:00:00 0377~ 99999 0377~ HEATHR~  51.5 -0.457    25  276.
## 6 2019-01-01 05:00:00 0377~ 99999 0377~ HEATHR~  51.5 -0.457    25  267.
## # ... with 17 more variables: ws <dbl>, ceil_hgt <dbl>, visibility <dbl>,
## #   air_temp <dbl>, dew_point <dbl>, atmos_pres <dbl>, RH <dbl>,
## #   cl_1 <dbl>, cl_1_height <dbl>, cl_2 <dbl>, cl_2_height <dbl>,
## #   cl_3 <dbl>, cl_3_height <dbl>, precip_12 <dbl>, precip_6 <dbl>,
## #   cl <dbl>, precip <dbl>

A função desc() permite a ordenação decrescente

dados_ordenados <- dados_heathrow %>% 
    arrange(desc(date))

head(dados_ordenados)
## # A tibble: 6 x 26
##   date                usaf  wban  code  station   lat    lon  elev    wd
##   <dttm>              <chr> <chr> <chr> <chr>   <dbl>  <dbl> <dbl> <dbl>
## 1 2019-09-14 21:00:00 0377~ 99999 0377~ HEATHR~  51.5 -0.456    25  215.
## 2 2019-09-14 20:00:00 0377~ 99999 0377~ HEATHR~  51.5 -0.457    25  229.
## 3 2019-09-14 19:00:00 0377~ 99999 0377~ HEATHR~  51.5 -0.457    25  224.
## 4 2019-09-14 18:00:00 0377~ 99999 0377~ HEATHR~  51.5 -0.457    25  224.
## 5 2019-09-14 17:00:00 0377~ 99999 0377~ HEATHR~  51.5 -0.457    25  231.
## 6 2019-09-14 16:00:00 0377~ 99999 0377~ HEATHR~  51.5 -0.457    25  207.
## # ... with 17 more variables: ws <dbl>, ceil_hgt <dbl>, visibility <dbl>,
## #   air_temp <dbl>, dew_point <dbl>, atmos_pres <dbl>, RH <dbl>,
## #   cl_1 <dbl>, cl_1_height <dbl>, cl_2 <dbl>, cl_2_height <dbl>,
## #   cl_3 <dbl>, cl_3_height <dbl>, precip_12 <dbl>, precip_6 <dbl>,
## #   cl <dbl>, precip <dbl>

Funções básicas de tratamento (dplyr) group_by():

A função group_by será bastante usada..

Quem conhece SQL pode estranhar um pouco o comportamento desta função, pois ela não agrupa os dados diminuindo o número de linhas imediatamente.

Mas veja que ela indica que há agrupamento

gini_agrupado <- gini %>% 
    select(country, date, value) %>% 
    group_by(country) 
    
gini_agrupado
## # A tibble: 682 x 3
## # Groups:   country [152]
##    country   date  value
##  * <chr>     <chr> <dbl>
##  1 Albania   2012   29  
##  2 Albania   2008   30  
##  3 Algeria   2011   27.6
##  4 Angola    2008   42.7
##  5 Argentina 2017   41.2
##  6 Argentina 2016   42  
##  7 Argentina 2014   41.7
##  8 Argentina 2013   41  
##  9 Argentina 2012   41.4
## 10 Argentina 2011   42.7
## # ... with 672 more rows

Funções básicas de tratamento (dplyr) group_by() (cont.):

Para várias operações, entretanto, o agrupamento faz com que o comportamento seja diferente

Uma operação bastante usada é numerar as linhas de um tibble.

No tibble agrupado, essa operação acontece em cada grupo.

gini_agrupado %<>% arrange(country, date) %>% 
    mutate(linha = row_number())


datatable(gini_agrupado)

Funções básicas de tratamento (dplyr) group_by() (cont.):

As funções lag() e lead() funcionam dentro de cada grupo (o primeiro value de um grupo não acessa o valor do outro grupo com lag() .

gini_agrupado %<>% mutate(
    value_ant = lag(value),
    delta_value = value - value_ant
    )

datatable(gini_agrupado)

Funções básicas de tratamento (dplyr) group_by() (cont.):

A função group_by só leva a uma sumarização, ou seja, só transforma o tibble em um tibble com o número de linhas igual ao número de grupos, quando executamos a função summarise()

maiores_temp_dia <- dados_heathrow %>% 
    group_by(date(date)) %>% 
    summarise(
        maxima = max(air_temp),
        minima = min(air_temp),
        media = mean(air_temp)
    )


datatable(maiores_temp_dia) %>% 
    formatRound(c("maxima", "minima", "media"), 1)

A função top_n retorna os n maiores valores. Se o tibble estiver agrupado, pra cada grupo.

maiores_temp_dia <- dados_heathrow %>% 
    group_by(date(date)) %>% 
    top_n(1, air_temp) %>% 
    ungroup() %>% 
    mutate(
        hora = hour(date),
        estacao = 
            case_when(
                month(date) %in% 1:3 ~ "Inverno",
                month(date) %in% 7:9 ~ "Verão",
                TRUE ~ "Outono/Primavera"
                
            )
    ) %>% 
    select(hora, estacao, air_temp)

ggplot(maiores_temp_dia) +
    geom_density( aes(x = hora, color = estacao )) +
    theme_light()

Leitura de dados de arquivos

Até agora acessamos dados que estavam disponíveis em bibliotecas, mas muitas vezes encontramos dados em arquivos.

De modo geral, as funções da biblioteca readr são mas rápidas do que as da biblioteca base, e também mostram barra de progresso no console. É possível reconhecê-las pelo _ ao invés de .

O portal da CVM é uma das minas de ouro de dados

O código abaixo baixa os dados que ainda não estão na nossa base

existem <- tibble(arquivo = list.files("dados/fundos"))


salva <- function(dado){
    
    endereco <- pull(dado, endereco)
    arquivo <- pull(dado, arquivo)
    
    print(endereco)
    conteudo <- read_csv2(endereco)
    
    write_csv(conteudo, paste0("dados/fundos/",arquivo))
    
}



baixa_faltantes <- tibble(data_dado = seq(ymd("2017-01-01"), by = "month", ymd(today()) )) %>% 
    mutate(data_formato = stamp_date("999912")(data_dado)) %>% 
    mutate(
        endereco = paste0(
            "http://dados.cvm.gov.br/dados/FI/DOC/INF_DIARIO/DADOS/",
            "inf_diario_fi_",
            data_formato,
            ".csv")) %>% 
    mutate(arquivo = paste("inf_diario_fi_",data_formato,".csv")) %>% 
    anti_join(existem, by = c("arquivo" = "arquivo")) %>% 
    select(-data_formato) %>% 
    group_by(data_dado) %>% 
    nest() %>% 
    mutate(data = map(data, salva ))


le_arquivo <- function(lista_arquivo){
    
    arquivo <- pull(lista_arquivo, arquivo)
    
    conteudo <- read_csv(arquivo)
    

    conteudo
    
}


todos_os_fundos <- tibble(arquivo = list.files("dados/fundos")) %>%
    mutate(arquivo = paste0("dados/fundos/",arquivo )) %>% 
    group_by(row_number()) %>% 
    nest() %>% 
    mutate(data = map(data, le_arquivo)) %>% 
    unnest()


head(todos_os_fundos)
## # A tibble: 6 x 9
## # Groups:   row_number() [1]
##   `row_number()` CNPJ_FUNDO DT_COMPTC  VL_TOTAL VL_QUOTA VL_PATRIM_LIQ
##            <int> <chr>      <date>        <dbl>    <dbl>         <dbl>
## 1              1 00.017.02~ 2017-01-02   1.08e8  2.43e13     108099858
## 2              1 00.017.02~ 2017-01-03   1.08e8  2.43e13     108144909
## 3              1 00.017.02~ 2017-01-04   1.08e8  2.43e13     108188649
## 4              1 00.017.02~ 2017-01-05   1.08e8  2.43e13     108234509
## 5              1 00.017.02~ 2017-01-06   1.08e8  2.43e13     108278954
## 6              1 00.017.02~ 2017-01-09   1.08e8  2.43e13     108321610
## # ... with 3 more variables: CAPTC_DIA <dbl>, RESG_DIA <dbl>,
## #   NR_COTST <dbl>
cadastro_fundos <- read_csv2("http://dados.cvm.gov.br/dados/FIE/CAD/DADOS/inf_cadastral_fie.csv", locale =  locale(encoding = "latin1") )
cotas_verde <- todos_os_fundos %>% 
    filter(CNPJ_FUNDO == "07.455.507/0001-89" )


cotas_verde
## # A tibble: 675 x 9
## # Groups:   row_number() [33]
##    `row_number()` CNPJ_FUNDO DT_COMPTC  VL_TOTAL VL_QUOTA VL_PATRIM_LIQ
##             <int> <chr>      <date>        <dbl>    <dbl>         <dbl>
##  1              1 07.455.50~ 2017-01-02  1.31e12  8.61e12 1312862006441
##  2              1 07.455.50~ 2017-01-03  1.31e12  8.62e12 1311291630745
##  3              1 07.455.50~ 2017-01-04  1.30e12  8.58e12 1305299654809
##  4              1 07.455.50~ 2017-01-05  1.29e12  8.55e12 1300213664841
##  5              1 07.455.50~ 2017-01-06  1.30e12  8.56e12 1301706718495
##  6              1 07.455.50~ 2017-01-09  1.30e12  8.55e12 1300644945751
##  7              1 07.455.50~ 2017-01-10  1.30e12  8.55e12 1300428226690
##  8              1 07.455.50~ 2017-01-11  1.30e12  8.53e12 1296492684946
##  9              1 07.455.50~ 2017-01-12  1.32e12  8.60e12 1332935151782
## 10              1 07.455.50~ 2017-01-13  1.33e12  8.61e12 1333437691636
## # ... with 665 more rows, and 3 more variables: CAPTC_DIA <dbl>,
## #   RESG_DIA <dbl>, NR_COTST <dbl>

Leitura de conteúdo de páginas WEB

Uma página WEB pode ser representada por uma árvore de objetos, também chamada de DOM (Document Object Model).

Esta árvore de objetos é definida pelo conteúdo de linguagem html que existe na página (e pode ser modificado por scripts em javascript e definições de estilo do CSS).

Podem existir objetos de vários tipos em uma página: links, inputs de dados, tabelas, células de tabelas, parágrafos, cabeçalhos etc.

Leitura de conteúdo de páginas WEB (cont.)

Para retirar da página web o conteúdo de que precisamos, temos que analisar como é esta árvore de objetos e que nós desta árvore nos interessam.

Imagine que queremos buscar dados na página de histórico de preços e taxas dos títulos brasileiros

A tecla F12 do Chrome nos permite ver a árvore DOM da página em que estamos navegando.

Leitura de conteúdo de páginas WEB (cont.)

É possível clicar com o botão direito e inspecionar um elementos específico de forma a saber onde ele está na árvore e que tipo de elemento ele é (mesmo que você saiba pouco de html).

O mais importante é saber que uma tag html que define um elemento tem a sintaxe:

<tipo_elemento nome_atributo=valor_attributo>texto do elemento</tipo_elemento>

Mesmo sem saber hmtl, fica claro que queremos esse tal de atributo href dos tais elementos a seja lá o que diabos isso seja (a é um link e href é o destino do link).

Leitura de conteúdo de páginas WEB (cont.)

A biblioteca rvest possibilita a extração destes elementos.

É possível caminhar pela árvore DOM até os nós desejados e atributos que queremos usando html_nodes e html_attr.

Munidos de uma função que faz download e salva um arquivo, podemos caminhar pelas planilhas e salvá-las

existem <- tibble(arquivo = list.files("dados/titulos"))


salva_planilha <- function(dado){
    
    arquivo <- pull(dado, endereco)
    destino <- pull(dado, name_in)
    
    download.file(
        arquivo, 
        paste0("dados/titulos/",destino,".xls" ),
        mode = "wb" ##PRA ARQUIVOS BINÁRIOS,
        )
    
}


links <- read_html("https://sisweb.tesouro.gov.br/apex/f?p=2031:2:0::::") %>% 
    html_nodes("body") %>% 
    html_nodes("a") %>% 
    html_attr("href") %>% 
    enframe(value = "endereco") %>%
    filter(str_detect(endereco, "cosis/sistd/obtem_arquivo/")) %>%
    mutate(destino = paste0(name,".xls")) %>% 
    anti_join(existem, by = c("destino" = "arquivo")) %>%
    select(-destino) %>% 
    mutate(endereco = paste0("https://sisweb.tesouro.gov.br/apex/",endereco) ) %>% 
    mutate(name_in = name) %>% 
    group_by(name) %>% 
    nest() %>% 
    mutate(data = map(data, salva_planilha ))

Leitura de conteúdo de planilhas Excel

Agora Vamos organizar as planilhas que lemos do site do tesouro.

Vamos usar a biblioteca read_xl.

Precisamos ler todas as sheets de todos os arquivos em um diretório (onde baixamos os arquivos excel do site do tesouro).

A função abaixo lê as sheets de um arquivo.

le_sheets <- function(dados){
    
    arquivo <- pull(dados, arquivo)
    excel_sheets(paste0("dados/titulos/",arquivo)) 
    
}

A função abaixo lê o conteúdo de cada sheet

le_conteudo_sheet <- function(dados){
    
    arquivo <- pull(dados, arquivo)
    sheet <- pull(dados, sheet)
    read_excel(
        paste0("dados/titulos/",arquivo),
        sheet = sheet,
        skip = 2,
        col_names = FALSE,
        col_types = "text"
    ) 
    
}

Leitura de conteúdo de planilhas Excel (cont.)

Munidos destas duas funções, podemos guardar tudo em um só dataframe.

Primeiro, definindo as sheets a ler

sheets_pra_ler <- list.files("dados/titulos") %>% 
    enframe(value = "arquivo") %>%
    mutate(arquivo_out = arquivo) %>% 
    group_by(name, arquivo_out) %>% 
    nest() %>% 
    mutate(data = map(data, le_sheets)) %>% 
    unnest() %>% 
    rename(
        arquivo = arquivo_out,
        sheet = data
    )

Depois lendo as sheets para um dataframe

sheets_lidas <- sheets_pra_ler %>% 
    mutate(titulo = str_extract(sheet,"[^[0-9]]*" )) %>% 
    mutate(vencimento = str_extract(sheet,"[0-9]{6}" )) %>% 
    mutate(vencimento = dmy(vencimento)) %>%
    mutate(
        arquivo_out = arquivo,
        sheet_out = sheet
    ) %>% 
    group_by(name, titulo, vencimento, arquivo_out, sheet_out) %>% 
    nest() %>% 
    mutate(data = map(data, le_conteudo_sheet)) %>% 
    unnest() %>% 
    ungroup()

Leitura de conteúdo de planilhas Excel (cont.)

Uma última arrumada

taxas_titulos <- sheets_lidas %>% 
    rename(
        data = 6,
        taxa_compra_manha = 7,
        taxa_venda_manha = 8,
        pu_compra_manha = 9,
        pu_venda_manha = 10,
        pu_base_manha = 11
    ) %>% 
    mutate(
        data = if_else(
            str_detect(data, "/"),
            dmy(data),
            as.numeric(data) + ymd("1899-12-31")
        )
    ) %>% 
    mutate(
        titulo = str_trim(titulo), 
        taxa_compra_manha = as.numeric(taxa_compra_manha),
        taxa_venda_manha = as.numeric(taxa_venda_manha),
        pu_compra_manha = as.numeric(pu_compra_manha),
        pu_venda_manha = as.numeric(pu_venda_manha),
        pu_base_manha = as.numeric(pu_base_manha)
    ) %>% 
    select(
        titulo,
        vencimento,
        data,
        taxa_compra_manha,
        taxa_venda_manha,
        pu_compra_manha,
        pu_venda_manha,
        pu_base_manha
    ) %>% 
    mutate(
        titulo = if_else(
            titulo == "NTN-B Principal", 
            "NTN-B Princ", 
            titulo
        )
    )

Leitura de conteúdo de planilhas Excel (cont.)

Aí fica fácil fazer a análise que desejarmos

ntnb_2045 <- taxas_titulos %>% 
    filter(
        titulo == "NTN-B Princ",
        vencimento == ymd("2045-05-15")
    ) %>% 
    mutate(taxa = (taxa_compra_manha +taxa_venda_manha)/2 )



ggplot(ntnb_2045) +
    geom_line(aes(x = data, y = taxa) ) +
    scale_x_date(
        date_breaks = "3 months",
        limits = c(ymd("2016-12-01"),NA),
        labels = date_format("%m/%y")
    ) +
    scale_y_continuous(
        labels = percent_format(accuracy = 0.1)
    ) + 
    labs(y = "NTN-B Principal 2045", x = "Data") +
    theme_light()

E quando o desenvolvedor da página web tá manguaçado?

Nem todas as páginas são fáceis de ler.

A página que mostra os DIs na BMF, por exemplo é esquisita:

O nosso objeto de interesse aparece na ferramenta chamada por F12, mas não é encontrada pela rvest ao ler o HTML

Lendo páginas difíceis

Existem duas técnicas para o Web Scraping:

Na página que visualizamos da BMF, o conteúdo recebido inclui um script javascript que popula a tabela, por isso não conseguimos reconhecer o conteúdo da tabela de pronto, pois ela só é carregada pela execução do script.

Lendo páginas difíceis (cont.)

Conseguimos, no entanto, extrair do script as informações de que precisamos

Lendo páginas difíceis (cont.)

O código abaixo extrai da página as informações de que precisamos.

Para extrair as informações, ele usa expressões regulares.

Repare na função que silencia um possível erro. Este erro pode acontecer se passarmos um dia que não tem dados. Precisamos disso pois vamos buscar todas as datas possíveis.

Essa não é a melhor forma de tratar um erro deste tipo. Veremos mais adiante

cotacoes <- tibble(
    id = 0:10,
    cotacao = c(
        "ajuste_ant",
        "ajuste_corrig",
        "preco_abert",
        "preco_min",
        "preco_max",
        "preco_med",
        "ult_preco",
        "ajuste",
        "var_pontos",
        "ult_of_compra",
        "ult_of_venda"
    )
)

#QUEM INVENTOU ISSO?
siglas_mes <- tibble(
    sigla_mes = c(
        "F",
        "G",
        "H",
        "J",
        "K",
        "M",
        "N",
        "Q",
        "U",
        "V",
        "X",
        "Z"
        ),
    mes = 1:12
)



pagina <- NA

try( 
    {pagina <- read_html("http://www2.bmf.com.br/pages/portal/bmfbovespa/boletim1/SistemaPregao1.asp?pagetype=pop&caminho=Resumo%20Estat%EDstico%20-%20Sistema%20Preg%E3o&Data=10/01/2017&Mercadoria=DI1") %>% 
    html_nodes("script") %>% 
    extract2(13) %>% 
    html_text()}
    ,
    silent = TRUE
)


linhas_impares <- str_extract_all(pagina, "MercFut1 \\+ '<td ALIGN=\"right\" CLASS=\"tabelaConteudo1\">[0-9.,]*") %>% 
    extract2(1) %>% 
    enframe() %>% 
    mutate(
        ativo = (row_number()-1) %/% 11 * 2,
        tipo_cotacao = (row_number() - 1) %% 11
    )


linhas_pares <- str_extract_all(pagina, "MercFut1 \\+ '<td ALIGN=\"right\" CLASS=\"tabelaConteudo2\">[0-9.,]*") %>% 
    extract2(1) %>% 
    enframe() %>% 
    mutate(
        ativo = (row_number()-1) %/% 11 * 2 + 1,
        tipo_cotacao = (row_number() - 1) %% 11
    )


linhas <- linhas_impares %>% 
    bind_rows(linhas_pares) %>%
    arrange(ativo) %>% 
    mutate(valor = str_extract_all(value, ">[0-9.,]*")) %>% 
    mutate(valor = str_sub(valor, 2)) %>% 
    select(ativo, tipo_cotacao, valor)



ativos <- str_extract_all(pagina, "MercFut3 = MercFut3 \\+ '</tr><td ALIGN=\"center\" CLASS=\"tabelaConteudo[0-9]\">[A-Z][0-9][0-9]") %>%
    extract2(1) %>% 
    enframe() %>% 
    mutate(
        nome_ativo = str_sub(value,-3),
        id = name - 1
    ) %>% 
    select(
        id,
        nome_ativo
    )


linhas %<>% left_join(ativos, by = c("ativo" = "id")) %>% 
    select(nome_ativo, tipo_cotacao, valor) %>% 
    mutate(
        sigla_mes = str_sub(nome_ativo, 1,1),
        ano = as.numeric(str_sub(nome_ativo, 2,4)) + 2000
    ) %>% 
    left_join(cotacoes, by = c("tipo_cotacao" = "id") ) %>% 
    left_join(siglas_mes, by = c("sigla_mes")) %>% 
    mutate(vencimento = make_date(ano, mes, 1)) %>%
    mutate(
        valor = parse_number(
            valor, 
            locale = locale(
                decimal_mark = ",", 
                grouping_mark = "." 
            )
        )
    ) %>% 
    select(
        nome_ativo,
        vencimento,
        cotacao,
        valor
    )
datatable(linhas)

Lendo páginas difíceis (cont.)

Agora vamos executar para todos os dias desde janeiro de 2017.

Preparamos a função…

le_uma_pagina_bmf <- function(dados){
    
    
    
    data <- pull(dados, data_in) %>% 
        stamp_date("31/01/2017")(.)
    

    
    pagina <- NA

    try( 
        {pagina <- read_html(paste0("http://www2.bmf.com.br/pages/portal/bmfbovespa/boletim1/SistemaPregao1.asp?pagetype=pop&caminho=Resumo%20Estat%EDstico%20-%20Sistema%20Preg%E3o&Data=",data,"&Mercadoria=DI1")) %>% 
        html_nodes("script") %>% 
        extract2(13) %>% 
        html_text()}
        ,
        silent = TRUE
    )
        
    
        
    if (!is.na(pagina)){
        
        
        
        linhas_impares <- str_extract_all(pagina, "MercFut1 \\+ '<td ALIGN=\"right\" CLASS=\"tabelaConteudo1\">[0-9.,]*") %>% 
            extract2(1) %>% 
            enframe() %>% 
            mutate(
                ativo = (row_number()-1) %/% 11 * 2,
                tipo_cotacao = (row_number() - 1) %% 11
            )
        
        
        linhas_pares <- str_extract_all(pagina, "MercFut1 \\+ '<td ALIGN=\"right\" CLASS=\"tabelaConteudo2\">[0-9.,]*") %>% 
            extract2(1) %>% 
            enframe() %>% 
            mutate(
                ativo = (row_number()-1) %/% 11 * 2 + 1,
                tipo_cotacao = (row_number() - 1) %% 11
            )
        
        
        linhas <- linhas_impares %>% 
            bind_rows(linhas_pares) %>%
            arrange(ativo) %>% 
            mutate(valor = str_extract_all(value, ">[0-9.,]*")) %>% 
            mutate(valor = str_sub(valor, 2)) %>% 
            select(ativo, tipo_cotacao, valor)
        
        
        
        ativos <- str_extract_all(pagina, "MercFut3 = MercFut3 \\+ '</tr><td ALIGN=\"center\" CLASS=\"tabelaConteudo[0-9]\">[A-Z][0-9][0-9]") %>%
            extract2(1) %>% 
            enframe() %>% 
            mutate(
                nome_ativo = str_sub(value,-3),
                id = name - 1
            ) %>% 
            select(
                id,
                nome_ativo
            )
        
        
        linhas %<>% left_join(ativos, by = c("ativo" = "id")) %>% 
            select(nome_ativo, tipo_cotacao, valor) %>% 
            mutate(
                sigla_mes = str_sub(nome_ativo, 1,1),
                ano = as.numeric(str_sub(nome_ativo, 2,4)) + 2000
            ) %>% 
            left_join(cotacoes, by = c("tipo_cotacao" = "id") ) %>% 
            left_join(siglas_mes, by = c("sigla_mes")) %>% 
            mutate(vencimento = make_date(ano, mes, 1)) %>%
            mutate(
                valor = parse_number(
                    valor, 
                    locale = locale(
                        decimal_mark = ",", 
                        grouping_mark = "." 
                    )
                )
            ) %>% 
            select(
                nome_ativo,
                vencimento,
                cotacao,
                valor
            )
        
        linhas
    }
    else
    {
        tibble(dia_inutil = TRUE )
    }
}

E executamos para todos os dias.

dados_todas_as_datas <- seq.Date(
        ymd("2017-01-01"), 
        to = today(), 
        by = "day") %>% 
    enframe() %>% 
    rename(data_out = value) %>% 
    mutate(data_in = data_out) %>% 
    group_by(data_out, name) %>% 
    nest_legacy() %>% 
    mutate(data = map(data, le_uma_pagina_bmf)) %>% 
    unnest_legacy() 

Lendo páginas difíceis (cont.)

O dataframe com todos os dados ficou assim.

Veja como as funções da biblioteca kable e kableExtra dão mais controle na criação das tabelas.

Note como ele ainda não está “Tidy”. Por quê?

dados_todas_as_datas %>%
    ungroup() %>% 
    filter(is.na(dia_inutil)) %>% 
    select(-dia_inutil, - name) %>% 
    head(n = 200) %>% 
    mutate(
        data_out = stamp_date("31/12/2010")(data_out),
        vencimento = stamp_date("12/%Y")(vencimento),
        valor = number(valor, accuracy = 0.001, decimal.mark = ",", big.mark = ".")
    ) %>% 
    kable(
        col.names = 
            c(
                "Data",
                "Ativo",
                "Vencimento",
                "Tipo Cotação",
                "Cotação"
            ),
        align = 
            c("l","l","l","l","r")
    ) %>% 
    kable_styling(
        
    ) %>% 
    row_spec(
        seq(2, 200, 2),
        background = "#eeeeee"
    ) 
Data Ativo Vencimento Tipo Cotação Cotação
02/01/2017 F17 01/2017 ajuste_ant 99.999,980
02/01/2017 F17 01/2017 ajuste_corrig 100.050,710
02/01/2017 F17 01/2017 preco_abert 0,000
02/01/2017 F17 01/2017 preco_min 0,000
02/01/2017 F17 01/2017 preco_max 0,000
02/01/2017 F17 01/2017 preco_med 0,000
02/01/2017 F17 01/2017 ult_preco 0,000
02/01/2017 F17 01/2017 ajuste 100.000,000
02/01/2017 F17 01/2017 var_pontos 0,020
02/01/2017 F17 01/2017 ult_of_compra 0,000
02/01/2017 F17 01/2017 ult_of_venda 0,000
02/01/2017 G17 02/2017 ajuste_ant 98.918,060
02/01/2017 G17 02/2017 ajuste_corrig 13,280
02/01/2017 G17 02/2017 preco_abert 13,270
02/01/2017 G17 02/2017 preco_min 13,280
02/01/2017 G17 02/2017 preco_max 13,272
02/01/2017 G17 02/2017 preco_med 13,271
02/01/2017 G17 02/2017 ult_preco 98.918,080
02/01/2017 G17 02/2017 ajuste 0,020
02/01/2017 G17 02/2017 var_pontos 13,271
02/01/2017 G17 02/2017 ult_of_compra 13,274
02/01/2017 G17 02/2017 ult_of_venda 97.013,860
02/01/2017 H17 03/2017 ajuste_ant 98.058,480
02/01/2017 H17 03/2017 ajuste_corrig 98.107,120
02/01/2017 H17 03/2017 preco_abert 13,150
02/01/2017 H17 03/2017 preco_min 13,150
02/01/2017 H17 03/2017 preco_max 13,156
02/01/2017 H17 03/2017 preco_med 13,154
02/01/2017 H17 03/2017 ult_preco 13,155
02/01/2017 H17 03/2017 ajuste 98.057,400
02/01/2017 H17 03/2017 var_pontos 1,080
02/01/2017 H17 03/2017 ult_of_compra 13,150
02/01/2017 H17 03/2017 ult_of_venda 13,160
02/01/2017 J17 04/2017 ajuste_ant 97.059,280
02/01/2017 J17 04/2017 ajuste_corrig 12,900
02/01/2017 J17 04/2017 preco_abert 12,885
02/01/2017 J17 04/2017 preco_min 12,925
02/01/2017 J17 04/2017 preco_max 12,906
02/01/2017 J17 04/2017 preco_med 12,910
02/01/2017 J17 04/2017 ult_preco 97.010,090
02/01/2017 J17 04/2017 ajuste 3,770
02/01/2017 J17 04/2017 var_pontos 12,900
02/01/2017 J17 04/2017 ult_of_compra 12,910
02/01/2017 J17 04/2017 ult_of_venda 95.282,740
02/01/2017 K17 05/2017 ajuste_ant 96.220,720
02/01/2017 K17 05/2017 ajuste_corrig 96.267,740
02/01/2017 K17 05/2017 preco_abert 12,745
02/01/2017 K17 05/2017 preco_min 12,735
02/01/2017 K17 05/2017 preco_max 12,745
02/01/2017 K17 05/2017 preco_med 12,741
02/01/2017 K17 05/2017 ult_preco 12,740
02/01/2017 K17 05/2017 ajuste 96.218,950
02/01/2017 K17 05/2017 var_pontos 1,770
02/01/2017 K17 05/2017 ult_of_compra 12,740
02/01/2017 K17 05/2017 ult_of_venda 12,755
02/01/2017 M17 06/2017 ajuste_ant 95.330,910
02/01/2017 M17 06/2017 ajuste_corrig 12,550
02/01/2017 M17 06/2017 preco_abert 12,550
02/01/2017 M17 06/2017 preco_min 12,550
02/01/2017 M17 06/2017 preco_max 12,550
02/01/2017 M17 06/2017 preco_med 12,550
02/01/2017 M17 06/2017 ult_preco 95.282,590
02/01/2017 M17 06/2017 ajuste 0,150
02/01/2017 M17 06/2017 var_pontos 12,550
02/01/2017 M17 06/2017 ult_of_compra 0,000
02/01/2017 M17 06/2017 ult_of_venda 93.605,300
02/01/2017 N17 07/2017 ajuste_ant 94.410,120
02/01/2017 N17 07/2017 ajuste_corrig 94.481,010
02/01/2017 N17 07/2017 preco_abert 12,380
02/01/2017 N17 07/2017 preco_min 12,340
02/01/2017 N17 07/2017 preco_max 12,400
02/01/2017 N17 07/2017 preco_med 12,349
02/01/2017 N17 07/2017 ult_preco 12,340
02/01/2017 N17 07/2017 ajuste 94.433,120
02/01/2017 N17 07/2017 var_pontos 23,000
02/01/2017 N17 07/2017 ult_of_compra 12,335
02/01/2017 N17 07/2017 ult_of_venda 12,350
02/01/2017 Q17 08/2017 ajuste_ant 93.674,050
02/01/2017 Q17 08/2017 ajuste_corrig 12,185
02/01/2017 Q17 08/2017 preco_abert 12,160
02/01/2017 Q17 08/2017 preco_min 12,185
02/01/2017 Q17 08/2017 preco_max 12,161
02/01/2017 Q17 08/2017 preco_med 12,160
02/01/2017 Q17 08/2017 ult_preco 93.626,570
02/01/2017 Q17 08/2017 ajuste 21,270
02/01/2017 Q17 08/2017 var_pontos 0,000
02/01/2017 Q17 08/2017 ult_of_compra 0,000
02/01/2017 Q17 08/2017 ult_of_venda 91.983,900
02/01/2017 U17 09/2017 ajuste_ant 92.722,560
02/01/2017 U17 09/2017 ajuste_corrig 92.798,300
02/01/2017 U17 09/2017 preco_abert 11,935
02/01/2017 U17 09/2017 preco_min 11,935
02/01/2017 U17 09/2017 preco_max 11,935
02/01/2017 U17 09/2017 preco_med 11,935
02/01/2017 U17 09/2017 ult_preco 11,935
02/01/2017 U17 09/2017 ajuste 92.751,270
02/01/2017 U17 09/2017 var_pontos 28,710
02/01/2017 U17 09/2017 ult_of_compra 0,000
02/01/2017 U17 09/2017 ult_of_venda 0,000
02/01/2017 V17 10/2017 ajuste_ant 92.043,710
02/01/2017 V17 10/2017 ajuste_corrig 11,850
02/01/2017 V17 10/2017 preco_abert 11,810
02/01/2017 V17 10/2017 preco_min 11,850
02/01/2017 V17 10/2017 preco_max 11,827
02/01/2017 V17 10/2017 preco_med 11,825
02/01/2017 V17 10/2017 ult_preco 91.997,060
02/01/2017 V17 10/2017 ajuste 13,160
02/01/2017 V17 10/2017 var_pontos 11,820
02/01/2017 V17 10/2017 ult_of_compra 11,825
02/01/2017 V17 10/2017 ult_of_venda 90.483,600
02/01/2017 X17 11/2017 ajuste_ant 91.212,090
02/01/2017 X17 11/2017 ajuste_corrig 91.295,710
02/01/2017 X17 11/2017 preco_abert 11,650
02/01/2017 X17 11/2017 preco_min 11,650
02/01/2017 X17 11/2017 preco_max 11,650
02/01/2017 X17 11/2017 preco_med 11,650
02/01/2017 X17 11/2017 ult_preco 11,650
02/01/2017 X17 11/2017 ajuste 91.249,440
02/01/2017 X17 11/2017 var_pontos 37,350
02/01/2017 X17 11/2017 ult_of_compra 0,000
02/01/2017 X17 11/2017 ult_of_venda 0,000
02/01/2017 Z17 12/2017 ajuste_ant 90.589,120
02/01/2017 Z17 12/2017 ajuste_corrig 11,540
02/01/2017 Z17 12/2017 preco_abert 11,540
02/01/2017 Z17 12/2017 preco_min 11,540
02/01/2017 Z17 12/2017 preco_max 11,540
02/01/2017 Z17 12/2017 preco_med 11,540
02/01/2017 Z17 12/2017 ult_preco 90.543,210
02/01/2017 Z17 12/2017 ajuste 59,610
02/01/2017 Z17 12/2017 var_pontos 0,000
02/01/2017 Z17 12/2017 ult_of_compra 0,000
02/01/2017 Z17 12/2017 ult_of_venda 87.645,570
02/01/2017 F18 01/2018 ajuste_ant 89.783,790
02/01/2017 F18 01/2018 ajuste_corrig 89.887,770
02/01/2017 F18 01/2018 preco_abert 11,510
02/01/2017 F18 01/2018 preco_min 11,435
02/01/2017 F18 01/2018 preco_max 11,510
02/01/2017 F18 01/2018 preco_med 11,459
02/01/2017 F18 01/2018 ult_preco 11,440
02/01/2017 F18 01/2018 ajuste 89.842,210
02/01/2017 F18 01/2018 var_pontos 58,420
02/01/2017 F18 01/2018 ult_of_compra 11,440
02/01/2017 F18 01/2018 ult_of_venda 11,450
02/01/2017 J18 04/2018 ajuste_ant 87.782,490
02/01/2017 J18 04/2018 ajuste_corrig 11,250
02/01/2017 J18 04/2018 preco_abert 11,210
02/01/2017 J18 04/2018 preco_min 11,250
02/01/2017 J18 04/2018 preco_max 11,225
02/01/2017 J18 04/2018 preco_med 11,220
02/01/2017 J18 04/2018 ult_preco 87.738,000
02/01/2017 J18 04/2018 ajuste 92,430
02/01/2017 J18 04/2018 var_pontos 0,000
02/01/2017 J18 04/2018 ult_of_compra 0,000
02/01/2017 J18 04/2018 ult_of_venda 83.304,290
02/01/2017 N18 07/2018 ajuste_ant 85.496,480
02/01/2017 N18 07/2018 ajuste_corrig 85.650,460
02/01/2017 N18 07/2018 preco_abert 11,120
02/01/2017 N18 07/2018 preco_min 11,060
02/01/2017 N18 07/2018 preco_max 11,130
02/01/2017 N18 07/2018 preco_med 11,085
02/01/2017 N18 07/2018 ult_preco 11,080
02/01/2017 N18 07/2018 ajuste 85.607,050
02/01/2017 N18 07/2018 var_pontos 110,570
02/01/2017 N18 07/2018 ult_of_compra 11,060
02/01/2017 N18 07/2018 ult_of_venda 11,080
02/01/2017 V18 10/2018 ajuste_ant 83.488,070
02/01/2017 V18 10/2018 ajuste_corrig 11,050
02/01/2017 V18 10/2018 preco_abert 11,000
02/01/2017 V18 10/2018 preco_min 11,050
02/01/2017 V18 10/2018 preco_max 11,015
02/01/2017 V18 10/2018 preco_med 11,010
02/01/2017 V18 10/2018 ult_preco 83.445,750
02/01/2017 V18 10/2018 ajuste 141,460
02/01/2017 V18 10/2018 var_pontos 0,000
02/01/2017 V18 10/2018 ult_of_compra 11,010
02/01/2017 V18 10/2018 ult_of_venda 79.189,040
02/01/2017 F19 01/2019 ajuste_ant 81.272,780
02/01/2017 F19 01/2019 ajuste_corrig 81.415,280
02/01/2017 F19 01/2019 preco_abert 11,000
02/01/2017 F19 01/2019 preco_min 10,930
02/01/2017 F19 01/2019 preco_max 11,030
02/01/2017 F19 01/2019 preco_med 10,968
02/01/2017 F19 01/2019 ult_preco 10,950
02/01/2017 F19 01/2019 ajuste 81.374,020
02/01/2017 F19 01/2019 var_pontos 101,240
02/01/2017 F19 01/2019 ult_of_compra 10,940
02/01/2017 F19 01/2019 ult_of_venda 10,950
02/01/2017 J19 04/2019 ajuste_ant 79.341,870
02/01/2017 J19 04/2019 ajuste_corrig 11,010
02/01/2017 J19 04/2019 preco_abert 10,970
02/01/2017 J19 04/2019 preco_min 11,010
02/01/2017 J19 04/2019 preco_max 11,002
02/01/2017 J19 04/2019 preco_med 10,980
02/01/2017 J19 04/2019 ult_preco 79.301,660
02/01/2017 J19 04/2019 ajuste 112,620
02/01/2017 J19 04/2019 var_pontos 0,000
02/01/2017 J19 04/2019 ult_of_compra 0,000
02/01/2017 J19 04/2019 ult_of_venda 74.943,830
02/01/2017 N19 07/2019 ajuste_ant 77.133,610
02/01/2017 N19 07/2019 ajuste_corrig 77.313,440

Pivoteando

Reparamos que o dataframe no slide anterior não está “Tidy”, ou seja não está de forma que cada linha represente um evento e cada coluna represente um atributo do evento.

Para nós, neste caso, um evento é formado por todas as informações de um ativo em um dia e não uma só das informações de um ativo em um dia.

Isso porque é extremamente comum fazermos contas com mais de uma informação do ativo em um dia (máxima - mínima, por exemplo)

Tidyr: pivot_longer e pivot_wider

O pacote Tidyr ajuda a arrumar os data frames dessas formas. O hex sticker dele é bem explicativo.

Os nomes das principais funções mudaram em setembro de 2019 (quando saiu a versão 1.0.0). Antes se chamavam gather() e spread() e agora se chamam pivot_wider() e pivot_longer(), o que é mais intuitivo.

Pivoteando nosso data frame com muitas linhas

O nosso data frame tem cada tipo de cotação em cada linha e gostaríamos que essas linhas fossem transformadas em colunas.

A função que faz isso se chama pivot_wider()

Seus parâmetros mais usados são:

dados_todas_as_datas %>% 
    pivot_wider(
        names_from = cotacao,
        values_from = valor
    ) %>% 
    str()
## Classes 'tbl_df', 'tbl' and 'data.frame':    25948 obs. of  17 variables:
##  $ data_out     : Date, format: "2017-01-01" "2017-01-02" ...
##  $ name         : int  1 2 2 2 2 2 2 2 2 2 ...
##  $ dia_inutil   : logi  TRUE NA NA NA NA NA ...
##  $ nome_ativo   : chr  NA "F17" "G17" "H17" ...
##  $ vencimento   : Date, format: NA "2017-01-01" ...
##  $ NA           : num  NA NA NA NA NA NA NA NA NA NA ...
##  $ ajuste_ant   : num  NA 100000 98918 98058 97059 ...
##  $ ajuste_corrig: num  NA 100050.7 13.3 98107.1 12.9 ...
##  $ preco_abert  : num  NA 0 13.3 13.2 12.9 ...
##  $ preco_min    : num  NA 0 13.3 13.2 12.9 ...
##  $ preco_max    : num  NA 0 13.3 13.2 12.9 ...
##  $ preco_med    : num  NA 0 13.3 13.2 12.9 ...
##  $ ult_preco    : num  NA 0 98918.1 13.2 97010.1 ...
##  $ ajuste       : num  NA 1.00e+05 2.00e-02 9.81e+04 3.77 ...
##  $ var_pontos   : num  NA 0.02 13.27 1.08 12.9 ...
##  $ ult_of_compra: num  NA 0 13.3 13.2 12.9 ...
##  $ ult_of_venda : num  NA 0 97013.9 13.2 95282.7 ...

Pivoteando nosso data frame com muitas colunas

É muito comum recebermos os dados com colunas que deviam ser valores de um atributo, e não um atributo em si.

O exemplo clássico é a colocação de datas nas colunas do dado, como nos dados retirados do site Datasus

read_csv2(
    "dados/siab/cadastro_numero_familias.csv", 
    skip = 3,
    locale = locale(encoding = "latin1" ) 
    ) %>% 
    glimpse()
## Observations: 5,502
## Variables: 19
## $ Município <chr> "110001 Alta Floresta D'Oeste", "110037 Alto Alegre ...
## $ `1998`    <chr> "2", "1224", "-", "3797", "676", "-", "-", "-", "529...
## $ `1999`    <chr> "3458", "2204", "546", "2763", "9922", "2268", "-", ...
## $ `2000`    <chr> "4668", "1911", "2002", "2832", "11790", "2615", "17...
## $ `2001`    <chr> "5078", "1994", "2348", "3924", "11648", "3061", "18...
## $ `2002`    <chr> "5078", "1976", "2348", "3907", "11654", "3732", "18...
## $ `2003`    <chr> "5077", "1804", "2021", "3463", "9931", "5499", "138...
## $ `2004`    <chr> "4925", "1766", "932", "4077", "18830", "7580", "180...
## $ `2005`    <chr> "5865", "1698", "1956", "4141", "14531", "7470", "18...
## $ `2006`    <chr> "6518", "3623", "1994", "4244", "16509", "6459", "19...
## $ `2007`    <chr> "6489", "3500", "2922", "1642", "17094", "7502", "19...
## $ `2008`    <chr> "7093", "3682", "3195", "4493", "16936", "7712", "23...
## $ `2009`    <chr> "6946", "3172", "3210", "4693", "16948", "7546", "23...
## $ `2010`    <chr> "6836", "3292", "1480", "1586", "16528", "7112", "25...
## $ `2011`    <chr> "6918", "3416", "2551", "3122", "16912", "7378", "18...
## $ `2012`    <chr> "6699", "3448", "3175", "4162", "18109", "7385", "19...
## $ `2013`    <chr> "6336", "3617", "3029", "4242", "19270", "9730", "19...
## $ `2014`    <chr> "6201", "3343", "3438", "4242", "14661", "9084", "19...
## $ `2015`    <chr> "6181", "3280", "-", "-", "6307", "-", "-", "-", "19...

Pivoteando nosso data frame com muitas colunas (cont.)

Acontece que “2009” não é um atributo, mas sim o valor de um atributo que deveria ser data

A função pivot_longe() faz a operação de que precisamos.

Note também a função separate(), que divide colunas de acordo com caracteres separadores.

siab_familias <- read_csv2(
    "dados/siab/cadastro_numero_familias.csv", 
    skip = 3,
    locale = locale(encoding = "latin1" ) 
    ) %>% 
    pivot_longer(
        cols = -`Município`,
        names_to = "data",
        values_to = "familias"
    ) %>% 
    rename(
        municipio = `Município`
    ) %>% 
    separate(
        col = municipio,
        into = c("cod_municipio", "municipio"),
        sep = " ",
        extra = "merge"  
    ) %>% 
    mutate(
        cod_municipio = as.integer(cod_municipio),
        data = as.integer(data),
        familias = as.integer(familias)
    )


head(siab_familias)
## # A tibble: 6 x 4
##   cod_municipio municipio              data familias
##           <int> <chr>                 <int>    <int>
## 1        110001 Alta Floresta D'Oeste  1998        2
## 2        110001 Alta Floresta D'Oeste  1999     3458
## 3        110001 Alta Floresta D'Oeste  2000     4668
## 4        110001 Alta Floresta D'Oeste  2001     5078
## 5        110001 Alta Floresta D'Oeste  2002     5078
## 6        110001 Alta Floresta D'Oeste  2003     5077

Pivoteando nosso data frame com muitas colunas (cont.)

Para pegar as UFs dos municipios, vamos pegar mais informações na Wikipedia

municipios <- read_html("https://pt.wikipedia.org/wiki/Lista_de_munic%C3%ADpios_do_Brasil_por_popula%C3%A7%C3%A3o") %>%
    html_nodes("table") %>% 
    extract2(1) %>% 
    html_table() %>% 
    rename(
        cod_municipio = `Código IBGE`,
        municipio = `Município`,
        UF = `Unidade federativa`,
        populacao = `População`
    ) %>% 
    mutate(
        populacao = str_remove_all(populacao,"[^0-9]") 
    ) %>% 
    mutate(populacao = as.integer(populacao))

glimpse(municipios)
## Observations: 5,570
## Variables: 5
## $ Posição       <chr> "1º", "2º", "3º", "4º", "5º", "6º", "7º", "8º", ...
## $ UF            <chr> "São Paulo", "Rio de Janeiro", "Distrito Federal...
## $ cod_municipio <int> 50308, 4557, 108, 27408, 4400, 6200, 2603, 6902,...
## $ municipio     <chr> "São Paulo", "Rio de Janeiro", "Brasília", "Salv...
## $ populacao     <int> 12252023, 6718903, 3015268, 2872347, 2669342, 25...

Funções de junção (dplyr)

Para juntar as informações de

Funções de pivot (tidyr)

Funções de pivot (tidyselect)

manobras com vars, starts_with, num_range etc

Tratamento de strings (caracteres) com stringr

Tratamento de datas (lubridate)

Tratamento de dados categóricos (forcats)

VISUALIZAÇÃO DE DADOS

PROGRAMAÇÃO FUNCIONAL

Simulação CFA com vários parâmetros

n_simul <- 10000
n_questoes <- 240
min_aprovacao <-  0.6
n_aprovado <- 240 * min_aprovacao
prob_questao <- 0.2


estimar_chance <-  function(...){
    
    print("aqui")
    fracao_eliminar_questoes <- as.vector(...)
    
    #definindo o número de questões 
    n_questoes_cada_elimina <- t(rmultinom(n_simul, size = n_questoes, fracao_eliminar_questoes))
    probs_quando_elimina <- 1/(5:1)
    acertos_concatenados <- 
        rbinom( 
            n =  n_simul * 5 , 
            size = as.vector(t(n_questoes_cada_elimina)), 
            prob = probs_quando_elimina  
        )
    
    matriz_acertos <- matrix(acertos_concatenados, byrow = TRUE, nrow = n_simul )
    
    acertos <- rowSums(matriz_acertos)
    sum(acertos > n_aprovado)/n_simul
}


#definindo a chance podermos eliminar 0, 1, 2, ... 4 alternativas
resultados <- RandVec(n = 5, m = 100) %>% 
    .$RandVecOutput %>% 
    t() %>% 
    as_tibble() %>% 
    mutate(id = row_number()) %>% 
    group_by(id) %>% 
    nest() %>% 
    mutate(data_fica = data) %>%     
    mutate(data = map(data, estimar_chance) ) %>% 
    unnest()
## Warning: `as_tibble.matrix()` requires a matrix with column names or a `.name_repair` argument. Using compatibility `.name_repair`.
## This warning is displayed once per session.
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## [1] "aqui"
## Warning: `cols` is now required.
## Please use `cols = c(data, data_fica)`

Aplicação de funções a dataframes (purrr)

map-reduce

EXECUTANDO MODELOS

Execução de modelos com broom

Execução de modelos com caret

SÉRIES TEMPORAIS

Repositórios de séries temporais

Manipulação de séries temporais

Biblioteca Forecast

COMUNICANDO OS RESULTADOS

Criando relatórios com R Markdown

Livros

Referência Bookdown

Criando visualizações interativas com Shiny